home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / pluginy Firefox / 1843 / 1843.xpi / content / firebug / breakpoint.js < prev    next >
Text File  |  2010-01-15  |  28KB  |  864 lines

  1. /* See license.txt for terms of usage */
  2.  
  3. FBL.ns(function() { with (FBL) {
  4.  
  5. // ************************************************************************************************
  6. // Constants
  7.  
  8. // ************************************************************************************************
  9.  
  10. Firebug.Breakpoint = extend(Firebug.Module,
  11. {
  12.     dispatchName: "breakpoints",
  13.  
  14.     toggleBreakOnNext: function(panel)
  15.     {
  16.         var breakable = Firebug.chrome.getGlobalAttribute("cmd_breakOnNext", "breakable");
  17.  
  18.         breakable = (breakable == "true" ? "false" : "true");
  19.         Firebug.chrome.setGlobalAttribute("cmd_breakOnNext", "breakable", breakable);
  20.  
  21.         // Call the current panel's logic related to break-on-next.
  22.         // If breakable == "true" the feature is currently disabled.
  23.         var enabled = (breakable == "true" ? false : true);
  24.         panel.breakOnNext(enabled);
  25.  
  26.         // Make sure the correct tooltip (coming from the current panel) is used.
  27.         this.updateBreakOnNextTooltips(panel);
  28.  
  29.         // Light up the tab whenever break on next is selected
  30.         this.updatePanelTab(panel, enabled);
  31.  
  32.         return enabled;
  33.     },
  34.  
  35.     showPanel: function(browser, panel)
  36.     {
  37.         if(!panel)  // there is no selectedPanel?
  38.             return;
  39.  
  40.         var breakButton = Firebug.chrome.$("fbBreakOnNextButton");
  41.         breakButton.removeAttribute("type");
  42.  
  43.         // Disable break-on-next if it isn't supported by the current panel.
  44.         if (!panel.breakable)
  45.         {
  46.             Firebug.chrome.setGlobalAttribute("cmd_breakOnNext", "breakable", "disabled");
  47.             Firebug.chrome.setGlobalAttribute("cmd_breakOnNext", "tooltiptext", "");
  48.             return;
  49.         }
  50.  
  51.         // Set the tooltips and update break-on-next button's state.
  52.         var shouldBreak = panel.shouldBreakOnNext();
  53.         this.updateBreakOnNextState(panel, shouldBreak);
  54.         this.updateBreakOnNextTooltips(panel);
  55.         this.updatePanelTab(panel, shouldBreak);
  56.  
  57.         var menuItems = panel.getBreakOnMenuItems();
  58.         if (!menuItems || !menuItems.length)
  59.             return;
  60.  
  61.         breakButton.setAttribute("type", "menu-button");
  62.  
  63.         var menuPopup = Firebug.chrome.$("fbBreakOnNextOptions");
  64.         eraseNode(menuPopup);
  65.  
  66.         for (var i=0; i<menuItems.length; ++i)
  67.             FBL.createMenuItem(menuPopup, menuItems[i]);
  68.     },
  69.  
  70.     updateBreakOnNextTooltips: function(panel)
  71.     {
  72.         var breakable = Firebug.chrome.getGlobalAttribute("cmd_breakOnNext", "breakable");
  73.  
  74.         // Get proper tooltip for the break-on-next button from the current panel.
  75.         // If breakable is set to "false" the feature is already activated (throbbing).
  76.         var armed = (breakable == "false");
  77.         var tooltip = panel.getBreakOnNextTooltip(armed);
  78.         if (!tooltip)
  79.             tooltip = "";
  80.  
  81.         Firebug.chrome.setGlobalAttribute("cmd_breakOnNext", "tooltiptext", tooltip);
  82.     },
  83.  
  84.     updateBreakOnNextState: function(panel, armed)
  85.     {
  86.         // If the panel should break at the next chance, set the button to not breakable,
  87.         // which means already active (throbbing).
  88.         var breakable = armed ? "false" : "true";
  89.         Firebug.chrome.setGlobalAttribute("cmd_breakOnNext", "breakable", breakable);
  90.     },
  91.  
  92.     updatePanelTab: function(panel, armed)
  93.     {
  94.         if (!panel)
  95.             return;
  96.  
  97.         var panelBar = Firebug.chrome.$("fbPanelBar1");
  98.         var tab = panelBar.getTab(panel.name);
  99.         if (tab)
  100.             tab.setAttribute("breakOnNextArmed", armed ? "true" : "false");
  101.     },
  102.  
  103.     breakNow: function(panel)
  104.     {
  105.         this.updatePanelTab(panel, false);
  106.         Firebug.Debugger.breakNow();
  107.     }
  108. });
  109.  
  110. // ************************************************************************************************
  111.  
  112. Firebug.Breakpoint.BreakpointListRep = domplate(Firebug.Rep,
  113. {
  114.     tag:
  115.         DIV({onclick: "$onClick", role : "list"},
  116.             FOR("group", "$groups",
  117.                 DIV({"class": "breakpointBlock breakpointBlock-$group.name", role: "listitem"},
  118.                     H1({"class": "breakpointHeader groupHeader"},
  119.                         "$group.title"
  120.                     ),
  121.                     DIV({"class": "breakpointsGroupListBox", role: "listbox"},
  122.                         FOR("bp", "$group.breakpoints",
  123.                             TAG("$bp|getBreakpointRep", {bp: "$bp"})
  124.                         )
  125.                     )
  126.                 )
  127.             )
  128.         ),
  129.  
  130.     getBreakpointRep: function(bp)
  131.     {
  132.         var rep = Firebug.getRep(bp);
  133.         return rep.tag;
  134.     },
  135.  
  136.     onClick: function(event)
  137.     {
  138.         var panel = Firebug.getElementPanel(event.target);
  139.  
  140.         if (getAncestorByClass(event.target, "breakpointCheckbox"))
  141.         {
  142.             var node = event.target.parentNode.getElementsByClassName("objectLink-sourceLink").item(0);
  143.             if (!node)
  144.                 return;
  145.  
  146.             var sourceLink = node.repObject;
  147.  
  148.             panel.noRefresh = true;
  149.             if (event.target.checked)
  150.                 fbs.enableBreakpoint(sourceLink.href, sourceLink.line);
  151.             else
  152.                 fbs.disableBreakpoint(sourceLink.href, sourceLink.line);
  153.             panel.noRefresh = false;
  154.         }
  155.         else if (getAncestorByClass(event.target, "closeButton"))
  156.         {
  157.             var sourceLink =
  158.                 event.target.parentNode.getElementsByClassName("objectLink-sourceLink").item(0).repObject;
  159.  
  160.             panel.noRefresh = true;
  161.  
  162.             var head = getAncestorByClass(event.target, "breakpointBlock");
  163.             var groupName = getClassValue(head, "breakpointBlock");
  164.             if (groupName == "breakpoints")
  165.                 fbs.clearBreakpoint(sourceLink.href, sourceLink.line);
  166.             else if (groupName == "errorBreakpoints")
  167.                 fbs.clearErrorBreakpoint(sourceLink.href, sourceLink.line);
  168.             else if (groupName == "monitors")
  169.             {
  170.                 fbs.unmonitor(sourceLink.href, sourceLink.line)
  171.             }
  172.  
  173.             var row = getAncestorByClass(event.target, "breakpointRow");
  174.             panel.removeRow(row);
  175.  
  176.             panel.noRefresh = false;
  177.         }
  178.     }
  179. });
  180.  
  181. // ************************************************************************************************
  182.  
  183. Firebug.Breakpoint.BreakpointRep = domplate(Firebug.Rep,
  184. {
  185.     tag:
  186.         DIV({"class": "breakpointRow focusRow", role: "option", "aria-checked": "$bp.checked"},
  187.             DIV({"class": "breakpointBlockHead"},
  188.                 INPUT({"class": "breakpointCheckbox", type: "checkbox",
  189.                     _checked: "$bp.checked", tabindex : '-1'}),
  190.                 SPAN({"class": "breakpointName"}, "$bp.name"),
  191.                 TAG(FirebugReps.SourceLink.tag, {object: "$bp|getSourceLink"}),
  192.                 IMG({"class": "closeButton", src: "blank.gif"})
  193.             ),
  194.             DIV({"class": "breakpointCode"}, "$bp.sourceLine")
  195.         ),
  196.  
  197.     getSourceLink: function(bp)
  198.     {
  199.         return new SourceLink(bp.href, bp.lineNumber, "js");
  200.     },
  201.  
  202.     supportsObject: function(bp)
  203.     {
  204.         return (bp instanceof Firebug.Debugger.Breakpoint);
  205.     }
  206. });
  207.  
  208. // ************************************************************************************************
  209.  
  210. Firebug.Breakpoint.BreakpointsPanel = function() {}
  211.  
  212. Firebug.Breakpoint.BreakpointsPanel.prototype = extend(Firebug.Panel,
  213. {
  214.     name: "breakpoints",
  215.     parentPanel: "script",
  216.     order: 2,
  217.  
  218.     initialize: function()
  219.     {
  220.         Firebug.Panel.initialize.apply(this, arguments);
  221.     },
  222.  
  223.     destroy: function(state)
  224.     {
  225.         Firebug.Panel.destroy.apply(this, arguments);
  226.     },
  227.  
  228.     initializeNode : function(oldPanelNode)
  229.     {
  230.         dispatch([Firebug.A11yModel], 'onInitializeNode', [this, 'console']);
  231.     },
  232.  
  233.     destroyNode : function()
  234.     {
  235.         dispatch([Firebug.A11yModel], 'onDestroyNode', [this, 'console']);
  236.     },
  237.  
  238.     show: function(state)
  239.     {
  240.         this.refresh();
  241.     },
  242.  
  243.     refresh: function()
  244.     {
  245.         if (this.noRefresh)
  246.             return;
  247.  
  248.         if (!Firebug.Debugger.isAlwaysEnabled(this.context))
  249.             this.updateScriptFiles(this.context);
  250.  
  251.         var extracted = this.extractBreakpoints(this.context, breakpoints, errorBreakpoints, monitors);
  252.  
  253.         var breakpoints = extracted.breakpoints;
  254.         var errorBreakpoints = extracted.errorBreakpoints;
  255.         var monitors = extracted.monitors;
  256.  
  257.         function sortBreakpoints(a, b)
  258.         {
  259.             if (a.href == b.href)
  260.                 return a.lineNumber < b.lineNumber ? -1 : 1;
  261.             else
  262.                 return a.href < b.href ? -1 : 1;
  263.         }
  264.  
  265.         breakpoints.sort(sortBreakpoints);
  266.         errorBreakpoints.sort(sortBreakpoints);
  267.         monitors.sort(sortBreakpoints);
  268.  
  269.         var groups = [];
  270.  
  271.         if (breakpoints.length)
  272.             groups.push({name: "breakpoints", title: $STR("Breakpoints"),
  273.                 breakpoints: breakpoints});
  274.         if (errorBreakpoints.length)
  275.             groups.push({name: "errorBreakpoints", title: $STR("ErrorBreakpoints"),
  276.                 breakpoints: errorBreakpoints});
  277.         if (monitors.length)
  278.             groups.push({name: "monitors", title: $STR("LoggedFunctions"),
  279.                 breakpoints: monitors});
  280.  
  281.         dispatch(Firebug.Debugger.fbListeners, "getBreakpoints", [this.context, groups]);
  282.  
  283.         if (groups.length)
  284.             Firebug.Breakpoint.BreakpointListRep.tag.replace({groups: groups}, this.panelNode);
  285.         else
  286.             FirebugReps.Warning.tag.replace({object: "NoBreakpointsWarning"}, this.panelNode);
  287.  
  288.         dispatch([Firebug.A11yModel], 'onBreakRowsRefreshed', [this, this.panelNode]);
  289.     },
  290.  
  291.     extractBreakpoints: function(context, breakpoints, errorBreakpoints, monitors)
  292.     {
  293.         var breakpoints = [];
  294.         var errorBreakpoints = [];
  295.         var monitors = [];
  296.  
  297.         var renamer = new SourceFileRenamer(context);
  298.         var self = this;
  299.         var Breakpoint = Firebug.Debugger.Breakpoint;
  300.  
  301.         for (var url in context.sourceFileMap)
  302.         {
  303.             fbs.enumerateBreakpoints(url, {call: function(url, line, props, script)
  304.             {
  305.                 if (renamer.checkForRename(url, line, props)) // some url in this sourceFileMap has changed, we'll be back.
  306.                     return;
  307.  
  308.                 if (script)  // then this is a current (not future) breakpoint
  309.                 {
  310.                     var analyzer = Firebug.SourceFile.getScriptAnalyzer(context, script);
  311.                     if (analyzer)
  312.                         var name = analyzer.getFunctionDescription(script, context).name;
  313.                     else
  314.                         var name = FBL.guessFunctionName(url, 1, context);
  315.                     var isFuture = false;
  316.                 }
  317.                 else
  318.                 {
  319.                     var isFuture = true;
  320.                 }
  321.  
  322.                 var source = context.sourceCache.getLine(url, line);
  323.                 breakpoints.push(new Breakpoint(name, url, line, !props.disabled, source, isFuture));
  324.             }});
  325.  
  326.             fbs.enumerateErrorBreakpoints(url, {call: function(url, line, props)
  327.             {
  328.                 if (renamer.checkForRename(url, line, props)) // some url in this sourceFileMap has changed, we'll be back.
  329.                     return;
  330.  
  331.                 var name = Firebug.SourceFile.guessEnclosingFunctionName(url, line, context);
  332.                 var source = context.sourceCache.getLine(url, line);
  333.                 errorBreakpoints.push(new Breakpoint(name, url, line, true, source));
  334.             }});
  335.  
  336.             fbs.enumerateMonitors(url, {call: function(url, line, props)
  337.             {
  338.                 if (renamer.checkForRename(url, line, props)) // some url in this sourceFileMap has changed, we'll be back.
  339.                     return;
  340.  
  341.                 var name = Firebug.SourceFile.guessEnclosingFunctionName(url, line, context);
  342.                 monitors.push(new Breakpoint(name, url, line, true, ""));
  343.             }});
  344.         }
  345.  
  346.         var result = null;
  347.  
  348.         if (renamer.needToRename(context))
  349.             result = this.extractBreakpoints(context); // since we renamed some sourceFiles we need to refresh the breakpoints again.
  350.         else
  351.             result = { breakpoints: breakpoints, errorBreakpoints: errorBreakpoints, monitors: monitors };
  352.  
  353.         // even if we did not rename, some bp may be dynamic
  354.         return result;
  355.     },
  356.  
  357.     getOptionsMenuItems: function()
  358.     {
  359.         var items = [];
  360.  
  361.         var context = this.context;
  362.         if (!Firebug.Debugger.isAlwaysEnabled(context))
  363.             this.updateScriptFiles(context);
  364.  
  365.         var bpCount = 0, disabledCount = 0;
  366.         var checkBoxes = this.panelNode.getElementsByClassName("breakpointCheckbox");
  367.         for (var i=0; i<checkBoxes.length; i++)
  368.         {
  369.             ++bpCount;
  370.             if (!checkBoxes[i].checked)
  371.                 ++disabledCount;
  372.         }
  373.  
  374.         if (disabledCount)
  375.         {
  376.             items.push(
  377.                 {label: "EnableAllBreakpoints",
  378.                     command: bindFixed(this.enableAllBreakpoints, this, context, true) }
  379.             );
  380.         }
  381.         if (bpCount && disabledCount != bpCount)
  382.         {
  383.             items.push(
  384.                 {label: "DisableAllBreakpoints",
  385.                     command: bindFixed(this.enableAllBreakpoints, this, context, false) }
  386.             );
  387.         }
  388.  
  389.         items.push(
  390.             "-",
  391.             {label: "ClearAllBreakpoints", disabled: !bpCount,
  392.                 command: bindFixed(this.clearAllBreakpoints, this, context) }
  393.         );
  394.  
  395.         return items;
  396.     },
  397.  
  398.     enableAllBreakpoints: function(context, status)
  399.     {
  400.         var checkBoxes = this.panelNode.getElementsByClassName("breakpointCheckbox");
  401.         for (var i=0; i<checkBoxes.length; i++)
  402.         {
  403.             var box = checkBoxes[i];
  404.             if (box.checked != status)
  405.                 this.click(box);
  406.         }
  407.     },
  408.  
  409.     clearAllBreakpoints: function(context)
  410.     {
  411.         this.noRefresh = true;
  412.  
  413.         var buttons = this.panelNode.getElementsByClassName("closeButton");
  414.         for (var i=0; i<buttons.length; i++)
  415.             this.click(buttons[i]);
  416.  
  417.         this.noRefresh = false;
  418.         this.refresh();
  419.     },
  420.  
  421.     click: function(node)
  422.     {
  423.         var doc = node.ownerDocument, event = doc.createEvent("MouseEvents");
  424.         event.initMouseEvent("click", true, true, doc.defaultView, 0, 0, 0, 0, 0,
  425.             false, false, false, false, 0, null);
  426.         return node.dispatchEvent(event);
  427.     },
  428.  
  429.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  430.  
  431.     removeRow: function(row)
  432.     {
  433.         row.parentNode.removeChild(row);
  434.  
  435.         var bpCount = countBreakpoints(this.context);
  436.         if (!bpCount)
  437.             this.refresh();
  438.     },
  439. });
  440.  
  441. // ************************************************************************************************
  442.  
  443. function countBreakpoints(context)
  444. {
  445.     var count = 0;
  446.     for (var url in context.sourceFileMap)
  447.     {
  448.         fbs.enumerateBreakpoints(url, {call: function(url, lineNo)
  449.         {
  450.             ++count;
  451.         }});
  452.     }
  453.     return count;
  454. }
  455.  
  456. // ************************************************************************************************
  457.  
  458. Firebug.Breakpoint.BreakpointGroup = function()
  459. {
  460.     this.breakpoints = [];
  461. }
  462.  
  463. Firebug.Breakpoint.BreakpointGroup.prototype =
  464. {
  465.     removeBreakpoint: function(bp)
  466.     {
  467.         remove(this.breakpoints, bp);
  468.     },
  469.  
  470.     enumerateBreakpoints: function(callback)
  471.     {
  472.         var breakpoints = cloneArray(this.breakpoints);
  473.         for (var i=0; i<breakpoints.length; i++)
  474.         {
  475.             var bp = breakpoints[i];
  476.             if (callback(bp))
  477.                 return true;
  478.         }
  479.         return false;
  480.     },
  481.  
  482.     findBreakpoint: function()
  483.     {
  484.         for (var i=0; i<this.breakpoints.length; i++)
  485.         {
  486.             var bp = this.breakpoints[i];
  487.             if (this.matchBreakpoint(bp, arguments))
  488.                 return bp;
  489.         }
  490.         return null;
  491.     },
  492.  
  493.     matchBreakpoint: function(bp, args)
  494.     {
  495.         // TODO: must be implemented in derived objects.
  496.         return false;
  497.     },
  498.  
  499.     isEmpty: function()
  500.     {
  501.         return !this.breakpoints.length;
  502.     }
  503. };
  504.  
  505. // ************************************************************************************************
  506.  
  507. function SourceFileRenamer(context)
  508. {
  509.     this.renamedSourceFiles = [];
  510.     this.context = context;
  511.     this.bps = [];
  512. }
  513.  
  514. SourceFileRenamer.prototype.checkForRename = function(url, line, props)
  515. {
  516.     var sourceFile = this.context.sourceFileMap[url];
  517.     if (sourceFile.isEval() || sourceFile.isEvent())
  518.     {
  519.         var segs = sourceFile.href.split('/');
  520.         if (segs.length > 2)
  521.         {
  522.             if (segs[segs.length - 2] == "seq")
  523.             {
  524.                 this.renamedSourceFiles.push(sourceFile);
  525.                 this.bps.push(props);
  526.             }
  527.         }
  528.         this.context.dynamicURLhasBP = true;  // whether not we needed to rename, the dynamic sourceFile has a bp.
  529.     }
  530.     else
  531.     {
  532.     }
  533.  
  534.     return (this.renamedSourceFiles.length > 0);
  535. };
  536.  
  537. SourceFileRenamer.prototype.needToRename = function(context)
  538. {
  539.     if (this.renamedSourceFiles.length > 0)
  540.         this.renameSourceFiles(context);
  541.  
  542.     return this.renamedSourceFiles.length;
  543. }
  544.  
  545. SourceFileRenamer.prototype.renameSourceFiles = function(context)
  546. {
  547.     for (var i = 0; i < this.renamedSourceFiles.length; i++)
  548.     {
  549.         var sourceFile = this.renamedSourceFiles[i];
  550.         var bp = this.bps[i];
  551.         FBTrace.sysout("debugger.renameSourceFiles type: "+bp.type, bp);
  552.         var oldURL = sourceFile.href;
  553.         var sameType = bp.type;
  554.         var sameLineNo = bp.lineNo;
  555.         var sameDebuggr = bp.debugger;
  556.  
  557.         var segs = oldURL.split('/');  // last is sequence #, next-last is "seq", next-next-last is kind
  558.         var kind = segs.splice(segs.length - 3, 3)[0];
  559.         var callerURL = segs.join('/');
  560.         var newURL = Firebug.Debugger.getURLFromMD5(callerURL, sourceFile.source, kind);
  561.         sourceFile.href = newURL.href;
  562.  
  563.         fbs.removeBreakpoint(bp.type, oldURL, bp.lineNo);
  564.         delete context.sourceFileMap[oldURL];  // SourceFile delete
  565.  
  566.         Firebug.Debugger.watchSourceFile(context, sourceFile);
  567.         var newBP = fbs.addBreakpoint(sameType, sourceFile, sameLineNo, bp, sameDebuggr);
  568.  
  569.         var panel = context.getPanel("script", true);
  570.         if (panel)
  571.         {
  572.             panel.context.invalidatePanels("breakpoints");
  573.             panel.renameSourceBox(oldURL, newURL.href);
  574.         }
  575.         if (context.sourceCache.isCached(oldURL))
  576.         {
  577.             var lines = context.sourceCache.load(oldURL);
  578.             context.sourceCache.storeSplitLines(newURL.href, lines);
  579.             context.sourceCache.invalidate(oldURL);
  580.         }
  581.  
  582.     }
  583.     return this.renamedSourceFiles.length;
  584. }
  585.  
  586. // ************************************************************************************************
  587.  
  588. Firebug.Breakpoint.ConditionEditor = function(doc)
  589. {
  590.     this.initialize(doc);
  591. }
  592.  
  593. Firebug.Breakpoint.ConditionEditor.prototype = domplate(Firebug.InlineEditor.prototype,
  594. {
  595.     tag:
  596.         DIV({"class": "conditionEditor"},
  597.             DIV({"class": "conditionEditorTop1"},
  598.                 DIV({"class": "conditionEditorTop2"})
  599.             ),
  600.             DIV({"class": "conditionEditorInner1"},
  601.                 DIV({"class": "conditionEditorInner2"},
  602.                     DIV({"class": "conditionEditorInner"},
  603.                         DIV({"class": "conditionCaption"}, $STR("ConditionInput")),
  604.                         INPUT({"class": "conditionInput", type: "text",
  605.                             "aria-label": $STR("ConditionInput")}
  606.                         )
  607.                     )
  608.                 )
  609.             ),
  610.             DIV({"class": "conditionEditorBottom1"},
  611.                 DIV({"class": "conditionEditorBottom2"})
  612.             )
  613.         ),
  614.  
  615.     initialize: function(doc)
  616.     {
  617.         this.box = this.tag.replace({}, doc, this);
  618.  
  619.         // XXXjjb we need childNode[1] always
  620.         this.input = this.box.childNodes[1].firstChild.firstChild.lastChild;
  621.         Firebug.InlineEditor.prototype.initialize.apply(this, arguments);
  622.     },
  623.  
  624.     show: function(sourceLine, panel, value)
  625.     {
  626.         this.target = sourceLine;
  627.         this.panel = panel;
  628.  
  629.         if (this.getAutoCompleter)
  630.             this.getAutoCompleter().reset();
  631.  
  632.         hide(this.box, true);
  633.         panel.selectedSourceBox.appendChild(this.box);
  634.  
  635.         if (this.input)
  636.             this.input.value = value;
  637.  
  638.         setTimeout(bindFixed(function()
  639.         {
  640.             var offset = getClientOffset(sourceLine);
  641.  
  642.             var bottom = offset.y+sourceLine.offsetHeight;
  643.             var y = bottom - this.box.offsetHeight;
  644.             if (y < panel.selectedSourceBox.scrollTop)
  645.             {
  646.                 y = offset.y;
  647.                 setClass(this.box, "upsideDown");
  648.             }
  649.             else
  650.                 removeClass(this.box, "upsideDown");
  651.  
  652.             this.box.style.top = y + "px";
  653.             hide(this.box, false);
  654.  
  655.             if (this.input)
  656.             {
  657.                 this.input.focus();
  658.                 this.input.select();
  659.             }
  660.         }, this));
  661.     },
  662.  
  663.     hide: function()
  664.     {
  665.         this.box.parentNode.removeChild(this.box);
  666.  
  667.         delete this.target;
  668.         delete this.panel;
  669.     },
  670.  
  671.     layout: function()
  672.     {
  673.     },
  674.  
  675.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  676.  
  677.     endEditing: function(target, value, cancel)
  678.     {
  679.         if (!cancel)
  680.         {
  681.             var sourceFile = this.panel.location;
  682.             var lineNo = parseInt(this.target.textContent);
  683.  
  684.             fbs.setBreakpointCondition(sourceFile, lineNo, value, Firebug.Debugger);
  685.         }
  686.     }
  687. });
  688.  
  689. // ************************************************************************************************
  690. /*
  691.  * Construct a break notification popup
  692.  * @param doc the document to contain the popup
  693.  * @param cause info object for the popup, with these optional fields:
  694.  *   strings: title, message, attrName
  695.  *   elements: target, relatedTarget: element
  696.  *   objects: prevValue, newValue
  697.  */
  698. Firebug.Breakpoint.BreakNotification = function(doc, cause)
  699. {
  700.     this.initialize(doc, cause);
  701. }
  702.  
  703. Firebug.Breakpoint.BreakNotification.prototype = domplate(Firebug.InlineEditor.prototype,
  704. {
  705.     tag:
  706.         DIV({"class": "conditionEditor breakNotification", onclick: "$hide"},
  707.             DIV({"class": "notationEditorTop1"},
  708.                 DIV({"class": "notationEditorTop2"})
  709.             ),
  710.             DIV({"class": "notationEditorInner1"},
  711.                 DIV({"class": "notationEditorInner2"},
  712.                     DIV({"class": "conditionEditorInner"},
  713.                         DIV({"class": "notationCaption"},
  714.                             SPAN({"class": "notationTitle"}, "$cause.title"),
  715.                             BUTTON({"class": "notationButton", onclick: "$onCopyAction",
  716.                                 $collapsed: "$cause|hideCopyAction"},
  717.                                 $STR("Copy")
  718.                             )
  719.                         ),
  720.                         DIV({"class": "notationCaption"},
  721.                             SPAN({"class": "notationTitle"}, "$cause|getTitle"),
  722.                             SPAN(" "),
  723.                             SPAN({"class": "notationTitle diff"}, "$cause|getDiff"),
  724.                             SPAN(" "),
  725.                             TAG("$cause|getTargetTag", {object: "$cause.target"}),
  726.                             SPAN(" "),
  727.                             TAG("$cause|getRelatedTargetTag", {object: "$cause.relatedNode"})
  728.                         )
  729.                     )
  730.                 )
  731.             ),
  732.             DIV({"class": "notationEditorBottom1"},
  733.                 DIV({"class": "notationEditorBottom2"})
  734.             )
  735.         ),
  736.  
  737.     getTargetTag: function(cause)
  738.     {
  739.         return cause.target ? FirebugReps.Element.shortTag : null;
  740.     },
  741.  
  742.     getRelatedTargetTag: function(cause)
  743.     {
  744.         return cause.relatedTarget ? FirebugReps.Element.shortTag : null;
  745.     },
  746.  
  747.     getDiff: function(cause)
  748.     {
  749.         var str = "";
  750.         if (cause.prevValue)
  751.             str += cropString(cause.prevValue, 40) + " -> ";
  752.         if (cause.newValue)
  753.             str += cropString(cause.newValue, 40);
  754.  
  755.         if (!str.length)
  756.             return "";
  757.  
  758.         if (!cause.target)
  759.             return str;
  760.  
  761.         return str;
  762.     },
  763.  
  764.     getTitle: function(cause)
  765.     {
  766.         var str = cause.message + (cause.attrName ? (" '"+cause.attrName+"'") : "");
  767.         if (this.getDiff(cause))
  768.             str += ":";
  769.         return str;
  770.     },
  771.  
  772.     initialize: function(doc, cause)
  773.     {
  774.         this.cause = cause;
  775.         this.box = this.tag.replace({cause: cause}, doc, this);
  776.     },
  777.  
  778.     show: function(sourceLine, panel, value)
  779.     {
  780.         this.target = sourceLine;
  781.         this.panel = panel;
  782.  
  783.         hide(this.box, true);
  784.         panel.selectedSourceBox.appendChild(this.box);
  785.  
  786.         setTimeout(bindFixed(function()
  787.         {
  788.             var offset = getClientOffset(sourceLine);
  789.  
  790.             var bottom = offset.y+sourceLine.offsetHeight;
  791.             var y = bottom - this.box.offsetHeight;
  792.             if (y < panel.selectedSourceBox.scrollTop)
  793.             {
  794.                 y = offset.y;
  795.                 setClass(this.box, "upsideDown");
  796.             }
  797.             else
  798.                 removeClass(this.box, "upsideDown");
  799.  
  800.             this.box.style.top = y + "px";
  801.             hide(this.box, false);
  802.         }, this));
  803.     },
  804.  
  805.     hide: function(event) // the argument event does not come thru??
  806.     {
  807.         if (this.panel)
  808.         {
  809.             var guts = this.box.getElementsByClassName("conditionEditorInner").item(0);
  810.             collapse(guts, true);  // as the box shrinks you don't want text to spill
  811.  
  812.             var msg = this.cause.message;
  813.             if (msg)
  814.             {
  815.                 var self = this;
  816.                 var delta = Math.max(20,Math.floor(self.box.clientWidth/20));
  817.                 var interval = setInterval(function slide(event)
  818.                 {
  819.                     if (self.box.clientWidth < delta)
  820.                     {
  821.                         clearNode(guts);
  822.  
  823.                         clearInterval(interval);
  824.                         self.box.parentNode.removeChild(self.box);
  825.                         self.target.setAttribute('title', msg);
  826.                         setClass(self.target, "noteInToolTip");
  827.                         delete self.target;
  828.                         delete self.panel;
  829.                     }
  830.                     else
  831.                         self.box.style.width = (self.box.clientWidth - delta)+"px";
  832.                 }, 15);
  833.             }
  834.             else
  835.             {
  836.                 delete this.target;
  837.                 delete this.panel;
  838.             }
  839.         }
  840.         // else we already called hide
  841.     },
  842.  
  843.     hideCopyAction: function(cause)
  844.     {
  845.         return !cause.copyAction;
  846.     },
  847.  
  848.     onCopyAction: function(event)
  849.     {
  850.         if (this.cause.copyAction)
  851.             this.cause.copyAction();
  852.     }
  853. });
  854.  
  855. // ************************************************************************************************
  856. // Registration
  857.  
  858. Firebug.registerPanel(Firebug.Breakpoint.BreakpointsPanel);
  859. Firebug.registerRep(Firebug.Breakpoint.BreakpointRep);
  860. Firebug.registerModule(Firebug.Breakpoint);
  861.  
  862. // ************************************************************************************************
  863. }});
  864.